// -*- mode:c++ -*-

// Copyright (c) 2015 RISC-V Foundation
// Copyright (c) 2017 The University of Virginia
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Robert Scheffel

////////////////////////////////////////////////////////////////////
//
// The RISC-V ISA decoder
//

decode QUADRANT default Custom::custom() {

    0x0: decode COPCODE {

        0x0: CI32Op::c_addi4spn({{
            imm = CIMM8<1:1> << 2 |
                  CIMM8<0:0> << 3 |
                  CIMM8<7:6> << 4 |
                  CIMM8<5:2> << 6;
        }}, {{
            if (machInst.all == 0)
                fault = make_shared<IllegalInstFault>("zero instruction");
            Rp2_uw = sp_uw + imm;
        }}, uint32_t);

        0x2: CompressedLoad32::c_lw({{
            offset = CIMM2<1:1> << 2 |
                     CIMM3 << 3 |
                     CIMM2<0:0> << 6;
        }}, {{
            Rp2_sw = Mem_sw;
        }}, {{
            EA = Rp1_uw + offset;
        }});
        0x6: CompressedStore32::c_sw({{
            offset = CIMM2<1:1> << 2 |
                     CIMM3 << 3 |
                     CIMM2<0:0> << 6;
        }}, {{
            Mem_uw = Rp2_uw;
        }}, {{
            EA = Rp1_uw + offset;
        }});
    }

    0x1: decode COPCODE {
        0x0: CI32Op::c_addi({{
            imm = CIMM5;
            if (CIMM1 > 0)
                imm |= ~((int32_t)0x1f);
        }}, {{
            if ((RC1 == 0) != (imm == 0)) {
                if (RC1 == 0) {
                    fault = make_shared<IllegalInstFault>("source reg x0");
                } else // imm == 0
                    fault = make_shared<IllegalInstFault>("immediate = 0");
            }
            Rc1_sw = Rc1_sw + imm;
        }});
        0x1: J32Op::c_jal({{
            int32_t offset = CJUMPIMM<3:1> << 1 |
                             CJUMPIMM<9:9> << 4 |
                             CJUMPIMM<0:0> << 5 |
                             CJUMPIMM<5:5> << 6 |
                             CJUMPIMM<4:4> << 7 |
                             CJUMPIMM<8:7> << 8 |
                             CJUMPIMM<6:6> << 10;
            if (CJUMPIMM<10:10> > 0)
                offset |= ~((int32_t)0x7ff);
            ra_uw = NPC_uw;
            NPC_uw = PC_uw + offset;
        }}, IsIndirectControl, IsUncondControl, IsCall);
        format CI32Op {
            0x2: c_li({{
                imm = CIMM5;
                if (CIMM1 > 0)
                    imm |= ~((uint32_t)0x1f)
            }}, {{
                if (RC1 == 0)
                    fault = make_shared<IllegalInstFault>("source reg x0");
                Rc1_sw = imm;
            }});
            0x3: decode RC1 {
                0x2: c_addi16sp({{
                    imm = CIMM5<4:4> << 4 |
                          CIMM5<0:0> << 5 |
                          CIMM5<3:3> << 6 |
                          CIMM5<2:1> << 7;
                    if (CIMM1 > 0)
                        imm |= ~((int32_t)0x1ff);
                }}, {{
                    if (imm == 0) {
                        fault = make_shared<IllegalInstFault>("immediate = 0");
                    }
                    sp_sw = sp_sw + imm;
                }});
                default: c_lui({{
                    imm = CIMM5 << 12;
                    if (CIMM1 > 0)
                        imm |= ~((int32_t)0x1ffff);
                }}, {{
                    if (RC1 == 0 || RC1 == 2) {
                        fault = make_shared<IllegalInstFault>("source reg x0");
                    }
                    if (imm == 0) {
                        fault = make_shared<IllegalInstFault>("immediate = 0");
                    }
                    Rc1_sw = imm;
                }});
            }
        }
        0x4: decode CFUNCT2HIGH {
            format CI32Op {
                0x0: c_srli({{
                    imm = CIMM5 | (CIMM1 << 5);
                }}, {{
                    if (imm == 0)
                        fault = make_shared<IllegalInstFault>("immediate = 0");
                    Rp1_uw = Rp1_uw >> imm;
                }}, uint32_t);
                0x1: c_srai({{
                    imm = CIMM5 | (CIMM1 << 5);
                }}, {{
                    if (imm == 0)
                        fault = make_shared<IllegalInstFault>("immediate = 0");
                    Rp1_sw = Rp1_sw >> imm;
                }}, uint32_t);
                0x2: c_andi({{
                    imm = CIMM5;
                    if (CIMM1 > 0)
                        imm |= ~((int32_t)0x1f);
                }}, {{
                    Rp1_uw = Rp1_uw & imm;
                }}, uint32_t);
            }
            format R32Op {
                0x3: decode CFUNCT1 {
                    0x0: decode CFUNCT2LOW {
                        0x0: c_sub({{
                            Rp1_sw = Rp1_sw - Rp2_sw;
                        }});
                        0x1: c_xor({{
                            Rp1_uw = Rp1_uw ^ Rp2_uw;
                        }});
                        0x2: c_or({{
                            Rp1_uw = Rp1_uw | Rp2_uw;
                        }});
                        0x3: c_and({{
                            Rp1_uw = Rp1_uw & Rp2_uw
                        }});
                    }
                }
            }
        }
        0x5: J32Op::c_j({{
            int32_t offset = CJUMPIMM<3:1> << 1 |
                             CJUMPIMM<9:9> << 4 |
                             CJUMPIMM<0:0> << 5 |
                             CJUMPIMM<5:5> << 6 |
                             CJUMPIMM<4:4> << 7 |
                             CJUMPIMM<8:7> << 8 |
                             CJUMPIMM<6:6> << 10;
            if (CJUMPIMM<10:10> > 0)
                offset |= ~((int32_t)0x7ff);
            NPC_uw = PC_uw + offset;
        }}, IsDirectControl, IsUncondControl, IsCall);
        format CB32Op {
            0x6: c_beqz({{
                if (Rp1_uw == 0)
                    NPC_uw = PC_uw + imm;
                else
                    NPC_uw = NPC_uw;
            }}, IsDirectControl, IsCondControl);
            0x7: c_bnez({{
                if (Rp1_uw != 0)
                    NPC_uw = PC_uw + imm;
                else
                    NPC_uw = NPC_uw;
            }}, IsDirectControl, IsCondControl);
        }
    }

    0x2: decode COPCODE {
        0x0: CI32Op::c_slli({{
            imm = CIMM5 | (CIMM1 << 5);
        }}, {{
            if (imm == 0)
                fault = make_shared<IllegalInstFault>("immediate = 0");
            if (RC1 == 0)
                fault = make_shared<IllegalInstFault>("source reg x0");
            Rc1_uw = Rc1_uw << imm;
        }}, uint32_t);
        0x2: CompressedLoad32::c_lwsp({{
            offset = CIMM5<4:2> << 2 |
                     CIMM1 << 5 |
                     CIMM5<1:0> << 6;
        }}, {{
            if (RC1 == 0)
                fault = make_shared<IllegalInstFault>("source reg x0");
            Rc1_sw = Mem_sw;
        }}, {{
            EA = sp_uw + offset;
        }});
        0x4: decode CFUNCT1 {
            0x0: decode RC2 {
                0x0: Jump32::c_jr({{
                    if (RC1 == 0)
                        fault = make_shared<IllegalInstFault>("source reg x0");
                    NPC_uw = Rc1_uw;
                }}, IsIndirectControl, IsUncondControl, IsCall);
                default: CR32Op::c_mv({{
                    if (RC1 == 0)
                        fault = make_shared<IllegalInstFault>("source reg x0");
                    Rc1_uw = Rc2_uw;
                }});
            }
            0x1: decode RC1 {
                0x0: System32Op::c_ebreak({{
                    if (RC2 != 0) {
                        fault = make_shared<IllegalInstFault>("source reg x1");
                    }
                    fault = make_shared<BreakpointFault>();
                }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
                default: decode RC2 {
                    0x0: Jump32::c_jalr({{
                        if (RC1 == 0) {
                            fault = make_shared<IllegalInstFault>(
                                "source reg x0");
                        }
                        ra_uw = NPC_uw;
                        NPC_uw = Rc1_uw;
                    }}, IsIndirectControl, IsUncondControl, IsCall);
                    default: R32Op::c_add({{
                        Rc1_sw = Rc1_sw + Rc2_sw;
                    }});
                }
            }
        }
        0x6: CompressedStore32::c_swsp({{
            offset = CIMM6<5:2> << 2 |
                     CIMM6<1:0> << 6;
        }}, {{
            Mem_uw = Rc2_uw;
        }}, {{
            EA = sp_uw + offset;
        }});

    }

    0x3: decode OPCODE {

        0x00: decode FUNCT3 {
            format Load32 {
                0x0: lb({{
                    Rd_sw = Mem_sb;
                }});
                0x1: lh({{
                    Rd_sw = Mem_sh;
                }});
                0x2: lw({{
                    Rd_sw = Mem_sw;
                }});
                0x4: lbu({{
                    Rd_uw = Mem_ub;
                }});
                0x5: lhu({{
                    Rd_uw = Mem_uh;
                }});
            }
        }

        0x03: decode FUNCT3 {
            format I32Op {
                0x0: fence({{
                }}, uint32_t, IsNonSpeculative, IsMemBarrier, No_OpClass);
                0x1: fence_i({{
                }}, uint32_t, IsNonSpeculative, IsSerializeAfter, No_OpClass);
            }
        }

        0x04: decode FUNCT3 {
            format I32Op {
                0x0: addi({{
                    Rd_sw = Rs1_sw + imm;
                }}, int32_t);
                0x1: slli({{
                    Rd_uw = Rs1_uw << SHAMT5;
                }});
                0x2: slti({{
                    Rd_uw = (Rs1_sw < imm) ? 1 : 0;
                }});
                0x3: sltiu({{
                    Rd_uw = (Rs1_uw < imm) ? 1 : 0;
                }}, uint32_t);
                0x4: xori({{
                    Rd_uw = Rs1_uw ^ imm;
                }}, uint32_t);
                0x5: decode SRTYPE {
                    0x0: srli({{
                        Rd_uw = Rs1_uw >> SHAMT5;
                    }});
                    0x1: srai({{
                        Rd_sw = Rs1_sw >> SHAMT5;
                    }});
                }
                0x6: ori({{
                    Rd_uw = Rs1_uw | imm;
                }}, uint32_t);
                0x7: andi({{
                    Rd_uw = Rs1_uw & imm;
                }}, uint32_t);
            }
        }

        0x05: U32Op::auipc({{
            Rd_uw = PC_uw + imm;
        }});

        0x08: decode FUNCT3 {
            format Store32 {
                0x0: sb({{
                    Mem_ub = Rs2_ub;
                }});
                0x1: sh({{
                    Mem_uh = Rs2_uh;
                }});
                0x2: sw({{
                    Mem_uw = Rs2_uw;
                }});
            }
        }

        0x0c: decode FUNCT3 {
            format R32Op {
                0x0: decode FUNCT7 {
                    0x0: add({{
                        Rd_sw = Rs1_sw + Rs2_sw;
                    }});
                    0x1: mul({{
                        Rd_uw = Rs1_sw * Rs2_sw;
                    }}, IntMultOp);
                    0x20: sub({{
                        Rd_sw = Rs1_sw - Rs2_sw;
                    }});
                }
                0x1: decode FUNCT7 {
                    0x0: sll({{
                        Rd_uw = Rs1_uw << Rs2<5:0>;
                    }});
                    0x1: mulh({{
                        bool negate = (Rs1_sw < 0) != (Rs2_sw < 0);

                        uint32_t Rs1_lo = (uint16_t)abs(Rs1_sw);
                        uint32_t Rs1_hi = (uint32_t)abs(Rs1_sw) >> 16;
                        uint32_t Rs2_lo = (uint16_t)abs(Rs2_sw);
                        uint32_t Rs2_hi = (uint32_t)abs(Rs2_sw) >> 16;

                        uint32_t hi = Rs1_hi * Rs2_hi;
                        uint32_t mid1 = Rs1_hi * Rs2_lo;
                        uint32_t mid2 = Rs1_lo * Rs2_hi;
                        uint32_t lo = Rs1_lo * Rs2_lo;
                        uint32_t carry = ((uint32_t)(uint16_t)mid1
                            + (uint32_t)(uint16_t)mid2 + (lo >> 16)) >> 16;

                        uint32_t res = hi +
                                       (mid1 >> 16) +
                                       (mid2 >> 16) +
                                       carry;
                        Rd_uw = negate ? ~res + (Rs1_sw * Rs2_sw == 0 ? 1 : 0)
                                       : res
                    }}, IntMultOp);
                }
                0x2: decode FUNCT7 {
                    0x0: slt({{
                        Rd_uw = (Rs1_sw < Rs2_sw) ? 1 : 0;
                    }});
                    0x1: mulhsu({{
                        bool negate = Rs1_sw < 0;

                        uint32_t Rs1_lo = (uint16_t)abs(Rs1_sw);
                        uint32_t Rs1_hi = (uint32_t)abs(Rs1_sw) >> 16;
                        uint32_t Rs2_lo = (uint16_t)Rs2_uw;
                        uint32_t Rs2_hi = Rs2_uw >> 16;

                        uint32_t hi = Rs1_hi * Rs2_hi;
                        uint32_t mid1 = Rs1_hi * Rs2_lo;
                        uint32_t mid2 = Rs1_lo * Rs2_hi;
                        uint32_t lo = Rs1_lo * Rs2_lo;
                        uint32_t carry = ((uint32_t)(uint16_t)mid1
                            + (uint32_t)(uint16_t)mid2 + (lo >> 16)) >> 16;

                        uint32_t res = hi +
                                       (mid1 >> 16) +
                                       (mid2 >> 16) +
                                       carry;
                        Rd_uw = negate ? ~res + (Rs1_sw * Rs2_uw == 0 ? 1 : 0)
                                       : res
                    }}, IntMultOp);
                }
                0x3: decode FUNCT7 {
                    0x0: sltu({{
                        Rd = (Rs1_uw < Rs2_uw) ? 1 : 0;
                    }});
                    0x1: mulhu({{
                        uint32_t Rs1_lo = (uint16_t)Rs1_uw;
                        uint32_t Rs1_hi = Rs1_uw >> 16;
                        uint32_t Rs2_lo = (uint16_t)Rs2_uw;
                        uint32_t Rs2_hi = Rs2_uw >> 16;

                        uint32_t hi = Rs1_hi * Rs2_hi;
                        uint32_t mid1 = Rs1_hi * Rs2_lo;
                        uint32_t mid2 = Rs1_lo * Rs2_hi;
                        uint32_t lo = Rs1_lo * Rs2_lo;
                        uint32_t carry = ((uint32_t)(uint16_t)mid1
                            + (uint32_t)(uint16_t)mid2 + (lo >> 16)) >> 16;

                        Rd_uw = hi + (mid1 >> 16) + (mid2 >> 16) + carry;
                    }}, IntMultOp);
                }
                0x4: decode FUNCT7 {
                    0x0: xor({{
                        Rd_uw = Rs1_uw ^ Rs2_uw;
                    }});
                    0x1: div({{
                        if (Rs2_sw == 0) {
                            Rd_sw = -1;
                        } else if (Rs1_sw == numeric_limits<int32_t>::min()
                                && Rs2_sw == -1) {
                            Rd_sw = numeric_limits<int32_t>::min();
                        } else {
                            Rd_sw = Rs1_sw / Rs2_sw;
                        }
                    }}, IntDivOp);
                }
                0x5: decode FUNCT7 {
                    0x0: srl({{
                        Rd_uw = Rs1_uw >> Rs2<5:0>;
                    }});
                    0x1: divu({{
                        if (Rs2_uw == 0) {
                            Rd_uw = numeric_limits<uint32_t>::max();
                        } else {
                            Rd_uw = Rs1_uw / Rs2_uw;
                        }
                    }}, IntDivOp);
                    0x20: sra({{
                        Rd_sw = Rs1_sw >> Rs2<5:0>;
                    }});
                }
                0x6: decode FUNCT7 {
                    0x0: or({{
                        Rd_uw = Rs1_uw | Rs2_uw;
                    }});
                    0x1: rem({{
                        if (Rs2_sw == 0) {
                            Rd_uw = Rs1_sw;
                        } else if (Rs1_sw == numeric_limits<int32_t>::min()
                                && Rs2_sw == -1) {
                            Rd_uw = 0;
                        } else {
                            Rd_uw = Rs1_sw % Rs2_sw;
                        }
                    }}, IntDivOp);
                }
                0x7: decode FUNCT7 {
                    0x0: and({{
                        Rd_uw = Rs1_uw & Rs2_uw;
                    }});
                    0x1: remu({{
                        if (Rs2_uw == 0) {
                            Rd_uw = Rs1_uw;
                        } else {
                            Rd_uw = Rs1_uw % Rs2_uw;
                        }
                    }}, IntDivOp);
                }
            }
        }

        0x0d: U32Op::lui({{
            Rd_uw = (uint32_t)imm;
        }});

        0x18: decode FUNCT3 {
            format B32Op {
                0x0: beq({{
                    if (Rs1_uw == Rs2_uw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
                0x1: bne({{
                    if (Rs1_uw != Rs2_uw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
                0x4: blt({{
                    if (Rs1_sw < Rs2_sw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
                0x5: bge({{
                    if (Rs1_sw >= Rs2_sw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
                0x6: bltu({{
                    if (Rs1_uw < Rs2_uw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
                0x7: bgeu({{
                    if (Rs1_uw >= Rs2_uw) {
                        NPC_uw = PC_uw + imm;
                    } else {
                        NPC_uw = NPC_uw;
                    }
                }}, IsDirectControl, IsCondControl);
            }
        }

        0x19: decode FUNCT3 {
            0x0: Jump32::jalr({{
                Rd_uw = NPC_uw;
                NPC_uw = (imm + Rs1_uw) & (~0x1);
            }}, IsIndirectControl, IsUncondControl, IsCall);
        }

        0x1b: J32Op::jal({{
            Rd_uw = NPC_uw;
            NPC_uw = PC_uw + imm;
        }}, IsDirectControl, IsUncondControl, IsCall);

        0x1c: decode FUNCT3 {
            format System32Op {
                0x0: decode FUNCT12 {
                    0x0: ecall({{
                        fault = make_shared<SyscallFault>();
                    }}, IsSerializeAfter, IsNonSpeculative, IsSyscall,
                        No_OpClass);
                    0x1: ebreak({{
                        fault = make_shared<BreakpointFault>();
                    }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
                    0x2: uret({{
                        USTATUS status = xc->readMiscReg(MISCREG_USTATUS);
                        // xie -> xpie
                        status.uie = status.upie;
                        // xpie -> 1
                        status.upie = 1;
                        xc->setMiscReg(MISCREG_USTATUS, status);
                        // restore process counter
                        NPC_uw = xc->readMiscReg(MISCREG_UEPC);
                    }}, IsReturn);
                    0x102: sret({{
                        SSTATUS status = xc->readMiscReg(MISCREG_SSTATUS);
                        // change priv lvl to xpp
                        xc->setMiscReg(MISCREG_PRV, status.spp);
                        // xie -> xpie
                        status.sie = status.spie;
                        // xpie -> 1
                        status.spie = 1;
                        // xpp -> U (0)
                        status.spp = 0;
                        xc->setMiscReg(MISCREG_SSTATUS, status);
                        // restore process counter
                        NPC_uw = xc->readMiscReg(MISCREG_SEPC)
                    }}, IsReturn);
                    0x302: mret({{
                        MSTATUS status = xc->readMiscReg(MISCREG_MSTATUS);
                        // change priv lvl to xpp
                        xc->setMiscReg(MISCREG_PRV, status.mpp);
                        // xie -> xpie
                        status.mie = status.mpie;
                        // xpie -> 1
                        status.mpie = 1;
                        // xpp -> U (0)
                        status.mpp = 0;
                        xc->setMiscReg(MISCREG_MSTATUS, status);
                        // restore process counter
                        NPC_uw = xc->readMiscReg(MISCREG_MEPC);
                    }}, IsReturn);
                }
            }
            format CSR32Op {
                0x1: csrrw({{
                    Rd_uw = xc->readMiscReg(csr);
                    xc->setMiscReg(csr, Rs1_uw);
                }}, IsNonSpeculative, No_OpClass);
                0x2: csrrs({{
                    Rd_uw = xc->readMiscReg(csr);
                    if (Rs1_uw != 0) {
                        xc->setMiscReg(csr, Rd_uw | Rs1_uw);
                    }
                }}, IsNonSpeculative, No_OpClass);
                0x3: csrrc({{
                    Rd_uw = xc->readMiscReg(csr);
                    if (Rs1_uw != 0) {
                        xc->setMiscReg(csr, Rd_uw & ~Rs1_uw);
                    }
                }}, IsNonSpeculative, No_OpClass);
                0x5: csrrwi({{
                    Rd_uw = xc->readMiscReg(csr);
                    xc->setMiscReg(csr, uimm);
                }}, IsNonSpeculative, No_OpClass);
                0x6: csrrsi({{
                    Rd_uw = xc->readMiscReg(csr);
                    if (uimm != 0) {
                        xc->setMiscReg(csr, Rd_uw | uimm);
                    }
                }}, IsNonSpeculative, No_OpClass);
                0x7: csrrci({{
                    Rd_uw = xc->readMiscReg(csr);
                    if (uimm != 0) {
                        xc->setMiscReg(csr, Rd_uw & ~uimm);
                    }
                }}, IsNonSpeculative, No_OpClass);
            }
        }
    }
}
